/*
 * Class ClientThread.
 */
package jhelp;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

/**
 * This class provides a network connection between end client of
 * {@link Client} type and {@link jhelp.Server} object. Every object of
 * this class may work in separate thread.
 * @author <strong >Y.D.Zakovryashin, 2009</strong>
 * @version 1.0
 * @see Client
 * @see jhelp.Server
 */
public class ClientThread implements JHelp, Runnable {

    /**
     *
     */
    private Server server;
    /**
     *
     */
    private Socket clientSocket;
    /**
     *
     */
    private ObjectInputStream input;
    /**
     *
     */
    private ObjectOutputStream output;

    /**
     * Creates a new instance of Client
     * @param server reference to {@link Server} object.
     * @param socket reference to {@link java.net.Socket} object for connection
     * with client application.
     */
    public ClientThread(Server server, Socket socket) {
        this.server = server;
        clientSocket = socket;
    }


    /**
     * The method defines main job cycle for the object.
     */
    public void run() {
        if (server == null || clientSocket == null) return;
        if (connect() != JHelp.OK) return;
        while (!Thread.currentThread().isInterrupted()) {
            Object inObj;
            try {
                inObj = input.readObject();
            } catch (ClassNotFoundException e) {
                System.err.println("Неизвестная команда получена от клиента");
                e.printStackTrace();
                continue;
            } catch (EOFException e) {
                System.out.println("Соединение с клиентом закрыто с удаленной стороны");
                break;
            } catch (IOException e) {
                System.err.println("Сетевая ошибка при приеме команды");
                e.printStackTrace();
                break;
            }
            System.out.println(inObj);

            Data command;
            if (inObj != null && inObj instanceof Data) {
                command = (Data)inObj;
                if (command.getOperation() != JHelp.DISCONNECT) {
                    command = getData(command);

                    try {
                        output.writeObject(command);
                    } catch (IOException e) {
                        System.err.println("Сетевая ошибка при передаче ответа клиенту");
                        e.printStackTrace();
                        break;
                    }
                } else break;
            }
            else {
                System.err.println("Неизвестная команда получена от клиента");
                command = new Data(JHelp.ERROR, null, null);
                try {
                    output.writeObject(command);
                } catch (IOException e) {
                    System.err.println("Сетевая ошибка при передаче ответа клиенту");
                    e.printStackTrace();
                    break;
                }
            }
        }
        System.out.println("close session for client: " + clientSocket.getRemoteSocketAddress());
        disconnect();

    }

    /**
     * Opens input and output streams for data interchanging with
     * client application.  The method uses default parameters.
     * @return error code. The method returns {@link JHelp#OK} if streams are
     * successfully opened, otherwise the method returns {@link JHelp#ERROR}.
     */
    public int connect() {
        try {
            output = new ObjectOutputStream(clientSocket.getOutputStream());
            input = new ObjectInputStream(clientSocket.getInputStream());
        } catch (SocketException e) {
            System.err.println("Сетевая ошибка");
            e.printStackTrace();
            return JHelp.ERROR;
        } catch (IOException e) {
            System.err.println("Сетевая ошибка");
            e.printStackTrace();
            return JHelp.ERROR;
        }

        return JHelp.OK;
    }

    /**
     * Opens input and output streams for data interchanging with
     * client application. This method uses parameters specified by parameter
     * <code>args</code>.
     * @param args defines properties for input and output streams.
     * @return error code. The method returns {@link JHelp#OK} if streams are
     * successfully opened, otherwise the method returns {@link JHelp#ERROR}.
     */
    public int connect(String[] args) {

        return connect();
    }

    /**
     * Transports {@link Data} object from client application to {@link Server}
     * and returns modified {@link Data} object to client application.
     * @param data {@link Data} object which was obtained from client
     * application.
     * @return modified {@link Data} object
     */
    public Data getData(Data data) {
        if (server != null) return server.getData(data);
        else return null;
    }

    /**
     * The method closes connection with client application.
     * @return error code. The method returns {@link JHelp#OK} if input/output 
     * streams and connection with client application was closed successfully,
     * otherwise the method returns {@link JHelp#ERROR}.
     */
    public int disconnect() {

        try {
            input.close();
            output.close();
            if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close();

        } catch (IOException e) {
            System.err.println("Сетевая ошибка при завершении работы потока клиента");
            e.printStackTrace();
            return JHelp.ERROR;
        }

        return JHelp.DISCONNECT;
    }
}
